package service

import (
	"context"
	"demo/domain/model"
	"demo/infra/common/constant"
	"demo/infra/common/utils"
	"demo/infra/db"
	"demo/infra/errors"
	"demo/infra/middleware/cache"
	"fmt"
	"strconv"

	"gorm.io/gorm"
)

// UrlMapService 是应用服务，协调领域逻辑
type UrlMapService struct {
	compressCodeRepository model.CompressCodeRepository
	urlMapRepo             model.UrlMapRepository
	urlMapRecordRepo       model.UrlMapReplaceRecordRepository
	domainConfRepository   model.DomainConfRepository
	snowflakeKeyGenerator  *utils.SnowflakeKeyGenerator
	cache                  *cache.UrlMapCacheManager
	domain                 string
	db                     *gorm.DB
}

// NewUrlMapService 构造函数
func NewUrlMapService(urlMapRepo model.UrlMapRepository, urlMapRecordRepo model.UrlMapReplaceRecordRepository,
	domainConfRepository model.DomainConfRepository, compressCodeRepository model.CompressCodeRepository,
	snowflakeKeyGenerator *utils.SnowflakeKeyGenerator, cache *cache.UrlMapCacheManager,
	domain string, db *gorm.DB) *UrlMapService {
	return &UrlMapService{urlMapRepo: urlMapRepo, urlMapRecordRepo: urlMapRecordRepo,
		domainConfRepository: domainConfRepository, compressCodeRepository: compressCodeRepository,
		snowflakeKeyGenerator: snowflakeKeyGenerator, cache: cache,
		domain: domain, db: db}
}

// CreateUrlMap 创建短映射
func (ums *UrlMapService) CreateUrlMap(ctx context.Context, longUrl string, desc string, source string, creator string) string {
	_, err := ums.domainConfRepository.FindByDomain(ctx, ums.domain)
	if err != nil {
		panic(errors.NewBizError("B0000", "域名不存在"))
	}
	compressCode := getAvailableCompressCode(ctx, ums)
	shortUrl := fmt.Sprintf("%s://%s/%s", "http", ums.domain, compressCode.CompressCode)
	var urlMap = &model.UrlMap{
		LongURL:        longUrl,
		Description:    desc,
		Creator:        creator,
		Source:         source,
		CompressCode:   compressCode.CompressCode,
		ShortURL:       shortUrl,
		ShortURLDigest: utils.Sha1Hex(shortUrl),
		LongURLDigest:  utils.Sha1Hex(longUrl),
		URLStatus:      constant.UrlMapStatusAvailable.Value(),
	}
	db.WithTransactionAndPanic(ums.db, ctx, func(ctx context.Context, tx *gorm.DB) error {
		ctx = context.WithValue(ctx, "tx", tx)
		if err := ums.urlMapRepo.Save(ctx, urlMap); err != nil {
			tx.Rollback()
			return errors.NewBizError("B0000", "保存短链映射失败")
		}
		compressCode.CodeStatus = 2
		if err := ums.compressCodeRepository.UpdateCodeStatus(ctx, compressCode); err != nil {
			tx.Rollback()
			return errors.NewBizError("B0000", "更新压缩码状态失败")
		}
		if err := ums.urlMapRecordRepo.Save(ctx, &model.UrlMapReplaceRecord{URLMapID: urlMap.ID}); err != nil {
			tx.Rollback()
			return errors.NewBizError("B0000", "保存短链映射记录失败")
		}
		return nil
	})
	return shortUrl

}

func (ums *UrlMapService) ReplaceUrlMap(ctx context.Context, id int64, longUrl string, desc string, editor string) {
	urlMap, err := ums.urlMapRepo.FindById(ctx, id)
	if err == nil {
		panic(errors.NewBizError("B0000", "查询短链记录失败"))
	}
	if urlMap == nil {
		panic(errors.NewBizError("B0000", "未找到对应的短链映射记录"))
	}
	if urlMap.LongURL != longUrl {
		db.WithTransactionAndPanic(ums.db, ctx, func(ctx context.Context, tx *gorm.DB) error {
			// 1. 将事务对象存入上下文（供Repo使用）
			ctx = context.WithValue(ctx, "tx", tx)
			if err := ums.urlMapRecordRepo.Save(ctx, &model.UrlMapReplaceRecord{
				URLMapID:   urlMap.ID,
				OldLongURL: urlMap.LongURL,
				NewLongURL: longUrl,
			}); err != nil {
				return errors.NewBizError("B0000", "保存短链映射记录失败")
			}
			urlMap.LongURL = longUrl
			urlMap.LongURLDigest = utils.Sha1Hex(longUrl)
			urlMap.Description = desc
			urlMap.Editor = editor
			urlMap.Version = urlMap.Version + 1
			if err := ums.urlMapRepo.Update(ctx, urlMap); err != nil {
				return errors.NewBizError("B0000", "更新短链映射失败")
			}
			// 业务逻辑成功，会自动提交事务
			return nil
		})
		ums.cache.RefreshUrlMapCache(ctx, urlMap)
	}

}

func getAvailableCompressCode(ctx context.Context, ums *UrlMapService) *model.CompressCode {
	compressCode, err := ums.compressCodeRepository.GetLatestAvailableCompressCode(ctx)
	if err == nil {
		return compressCode
	} else {
		generateBatchCompressCodes(ctx, ums)
		compressCode, _ := ums.compressCodeRepository.GetLatestAvailableCompressCode(ctx)
		return compressCode
	}
}

func generateBatchCompressCodes(ctx context.Context, ums *UrlMapService) {
	var codes []model.CompressCode
	for i := 0; i < 10; i++ {
		sequence := ums.snowflakeKeyGenerator.GenerateKey()
		encoded := utils.Encode62(sequence)
		// 取最后 6 位
		code := encoded
		if len(encoded) > 6 {
			code = encoded[len(encoded)-6:]
		}
		codes = append(codes, model.CompressCode{
			SequenceValue: strconv.FormatInt(sequence, 10),
			CompressCode:  code,
			CodeStatus:    1,
		})
	}
	ums.compressCodeRepository.BatchSave(ctx, codes)
}
